(function($){
//var CURRENT_HOST = window.location.host
//获取话题数据与发布回复的URL前缀,用于本机测试切换
//var URLPRE = 'http://'+CURRENT_HOST+'/group/topic/';
var URLPRE = 'http://www.douban.com/group/topic/';
//最大请求页数,避免回复数过多的话题造成大量请求
//超过此页码的话题将只请求第一页与最后页
var MAX_GET_PAGE = 2;

/**
 * Ajax.get的快捷方式
 * @param {Object} url 请求url
 * @param {Object} cb 回调函数
 */
$.ajax.get = function(url, cb){
    $.ajax({
        type     : 'GET',
        url      : url,
        dataType : 'html',
        cache    : false,
        success  : function(r){
            cb(r);
        }
    });
};

function $j(s){
    return document.getElementById(s);
}

/**
 * 分发器类
 */
function Deliver(){
    var _procs = {};
    var _default = null;
    //Register a processor
    this.reg = function(name,fn,asDefault){
        _procs[name] = fn;
        if(asDefault){
            _default = fn;
        }
    };
    //Deliver a processor    
    this.get = function(name){
        return _procs[name] || _default;
    };
    //Call all processors
    this.each = function(){
        var args = arguments;
        jQuery.each(_procs, function(name, fn){
            fn.apply(fn,args);
        })
    };    
}    

/**
 * 数据获取类
 * @param {Object} id 帖子ID
 * @param {Object} cb 数据获取后的回调函数
 * @param {Object} userOptions 用户参数
 */
function Getter(id, cb, userOptions){
    /**
     * 是否正在运行
     */
    var running = false;
    
    /**
     * 定时器
     */
    var runTimer = null;
    
    /**
     * 帖子ID
     */
    var topicId = id;
    
    /**
     * 数据获取后的回调函数
     */
    var callback = cb;
    
    /**
     * 总页数
     */
    var totalPage = 0;
    
    /**
     * 当前获取的页码,从1开始
     */
    var curPage = 1;
    
    /**
     * 默认参数
     */
    var options = {
        //帖子url前缀
        urlPre : URLPRE,
        //数据获取周期
        period : 5000
    };
    jQuery.extend(options, userOptions);
    
    /**
     * 数据处理器数组
     */
    var processers = new Deliver();
    
    /**
     * 数据处理函数
     * 在次函数中会依次调用各个数据处理器,并返回处理后的topic对象
     * topic结构:
     * {
     * //作者数组
     * author : [
     *       {
     *         //用户名
     *         name : 'john',
     *      //用户头像
     *      img  : 'xxx.jpg',
     *      //用户链接
     *      link : '/pepole/john/'
     *       },...
     *  ],
     *  //内容数组
     *  contents : [
     *    'xxxx','xxxxx',...
     *  ],
     *  //更新时间数组
     *  updates : [
     *    '2009-01-12 11:11:11',....
     *  ]
     * }
     * @param {Object} r 获取到的html内容
     */
    function process(r){
        var topic = {
            authors  : [],
            authorsMap : {},
            contents : [],
            updates  : []
        };
        var mail = _mailProc(r);
        if (curPage == 1){
            //在第一页的数据中分析总页数
            totalPage = _pagesProc(r);
        }
        //将r为参数,topic为数据引用传递
        //依次调用分析器处理数据
        processers.each(topic, r);
        if (curPage < totalPage){
            if(totalPage <= MAX_GET_PAGE){
                //如果总页数小于最大请求页数
                //则依次请求余下页数
                curPage++;
            }
            else{
                //否则直接请求最后页
                curPage = totalPage;
            }
            getByPage(curPage);
            
        }
        running = false;
        return callback(topic,mail);
    }

    /**
     * 获取帖子页数
     * @param {Object} r
     */
    function _pagesProc(r){
        var re = /<div class="paginator">((?:.|\\n|\\r)*?)<\/div>/i;
        var matches = r.match(re);
        if ( ! matches){
            return 1;
        }
        else{
            //利用jQuery直接以分页层的html内容建立htmlfragment
            //然后利用dom方法获取倒数第二个链接(也就是最大页链接)的innerHTML
            //得到的结果即为总页码数
            var container = $(matches[0]);
            var as = $('a',container);
            var lastPageLink = as.eq(as.size() - 2);
            var ret = parseInt(lastPageLink.html());
            return ret;
        }
    }
    
    /**
     * 获取豆邮数
     * @param {Object} r
     */
    function _mailProc(r){
        var re = /\u8C46\u90AE<span style="color:#006600;">\((\d+)\)<\/span>/i;
        var matches = r.match(re);
        if ( ! matches){
            return 0;
        }
        else{
            return parseInt(matches[1]);
        }
    }
    
    /**
     * 共同处理器,提取了作者,时间,内容处理器中重复的代码
     * 先根据pattern获取全局中的匹配内容,
     * 再根据pattern获取局部匹配内容,
     * 最后将局部匹配的结果作为参数调用回调函数
     * @param {Object} r
     * @param {Object} pattern
     * @param {Object} mode
     * @param {Object} cb
     */
    function _commonProc(r, pattern, mode, cb){
        var reg = new RegExp(pattern, mode + 'ig');
        var re  = new RegExp(pattern, mode + 'i');
        var matches = r.match(reg);
        if ( ! matches){
            return false;
        }
        jQuery.each(matches, function(i, match){
            var matches = match.match(re);
            cb(matches);
        });
    }
    
    /**
     * 提取作者信息(名字与豆瓣页面连接)
     * 其中第一条为楼主信息,余下为回复者信息
     * 开始的时候是使用作者头像的部分(如下)获取所有的用户信息
     * <img alt="黑眼刀豆" src="http://otho.douban.com/icon/u1670189-2.jpg" class="pil"/>
     * 但是一些旧的豆瓣用户未设置头像,导致程序出错,所以改为使用这种方法获取
     * 用户的头像信息使用_authorDetailProc处理器获取
     * @param {Object} topic
     * @param {Object} r
     */    
    function _authorProc(topic, r){
        _commonProc(
            r,
            '<a href="(/people/(?:[^"]*?))">([^<]*?)</a>(?:\\s*\\(([^\\)]*?)\\))?',
            '',
            function(matches){
                topic.authors[topic.authors.length] = {
                    name : matches[2],
                    link : matches[1],
                    city : matches[3]                
                };                
            }
        );
    }
    processers.reg('author',_authorProc);
    
    /**
     * 提取作者详细信息
     * 暂时使用的详细信息有:作者头像信息
     * @param {Object} topic
     * @param {Object} r
     */
    function _authorDetailProc(topic, r){
        _commonProc(
            r,
            '<a href="([^"]*?)"><img class="pil" src="([^"]*?)" alt="([^"]*?)"/></a>',
            '',
            function(matches){
                //name : matches[3],
                //img  : matches[2],
                //link : matches[1]
                topic.authorsMap['_'+matches[1]+'_'] = {
                    img  : matches[2]
                };                
            }
        );        
    }
    processers.reg('authorDetail',_authorDetailProc);
    
    /**
     * 提取内容信息
     * 其中第一条为主题贴,余下为回复贴
     * @param {Object} topic
     * @param {Object} r
     */
    function _subjectProc(topic, r){
        _commonProc(
            r,
            '<p>((?:.|\\n|\\r)*?)</p>',
            'm',
            function(matches){
                topic.contents[topic.contents.length] = matches[1];
            }
        );
    }
    processers.reg('subject', _subjectProc);
    
    /**
     * 提取帖子时间信息
     * 其中第一条为主题发布时间,余下为回复时间
     * @param {Object} topic
     * @param {Object} r
     */
    function _updateProc(topic, r){
        var lzUpdate = /<span class="mn|color-green">([^<]*?)<\/span>/i;
        var matches = r.match(lzUpdate);
        if (matches){
            //如果是获取多页结果,则只有第一页有主题发表时间
            topic.updates[0] = matches[1];
        }
        _commonProc(
        r,
        '<h4>(.*)(?:\\r|\\s|\\n)*<a(?:.*)</a>',
        'm',
        function(matches){
            topic.updates[topic.updates.length] = matches[1];
        }
        );
    }
    processers.reg('update', _updateProc);
    
    /**
     * 根据页码获取帖子
     * @param {Object} p
     */
    function getByPage(p){
        curPage = p;
        var url = options.urlPre + topicId + '/';;
        if (curPage > 1){
            url = url + '?start=' + ((curPage - 1) * 100)
        }
        $.ajax.get(url, process);
    }
    
    /**
     * 执行获取数据操作
     */    
    function run(){
        if (running){
            return;
        }
        running = true;
        getByPage(1);
    }
    
    /**
     * 开始获取数据
     */
    this.start = function(){
        run();
        /*
        runTimer = setInterval(function(){
            run();
        }, options.period)
        */
    };
    
    /**
     * 停止获取数据
     */
    this.stop = function(){
        clearInterval(runTimer);
    };
    
    /**
     * 返回总页码数
     */
    this.getTotalPage = function(){
        return totalPage;
    }
    
    /**
     * 供Sender调用的更新接口
     * 当Sender发表评论成功后
     * 将返回的页面内容作为参数调用此接口来更新聊天内容
     * @param {Object} r
     */
    this.process = function(r){
        process(r);
    }
}

/**
 * 数据更新器
 * 根据最近帖子更新时间,将最新发布的帖子更新到页面相应位置
 * @param {Object} userOptions 用户参数
 */
function Updater(userOptions){
    /**
     * 最近更新的帖子时间
     */
    var lastUpdate = 0;
    
    var _bottomPos = -1;
    
    /**
     * 滚动条控制
     * 此函数主要实现当向上滚动浏览聊天记录超过一屏时
     * 聊天列表不自动滚动到最底处
     * bug:碰到Firefox的一个bug,此功能开发停止
     * 总是在调用yscroll函数开始处,滚动条就已经到了第一页处
     * 并且_bottomPos被赋值...
     */
    function yscroll(){
        var board_content = $j('banliao_content');
        board_content.scrollTop = board_content.scrollHeight;
        return;
        /*
         * bug:碰到Firefox的一个bug,此功能开发停止
        //当前可视区域高度
        var viewHeight = board_content.clientHeight;        
        //当前层高度
        var curScrollHeight = board_content.scrollHeight;
        //当前可视区域与层顶端的绝对高度
        var curPos = board_content.scrollTop;        

        var toPos = curScrollHeight - viewHeight;
        if (_bottomPos < 0 || (toPos - curPos) < viewHeight){
            board_content.scrollTop = toPos;
        }
        _bottomPos = toPos;
        */        
    }
    
    /**
     * 执行更新操作
     * @param {Object} topic 数据获取器返回的帖子对象
     * @param {Int} mail 未读豆邮数
     */
    this.update = function(topic, mail){
        if (mail){
            $('#banliao_mailnotifier').html('豆邮('+mail+')').fadeIn('slow');
        }
        else{
            $('#banliao_mailnotifier').html('豆邮').hide();
        }
        var html = [];
        jQuery.each(topic.updates, function(i, ts){
            //将'2009-01-12 11:11:11'的格式换为'2009/01/12 11:11:11'格式
            ts = ts.replace(/\-/g, '/');
            //将时间串转为时间戳
            var d = new Date(ts);
            var time = d.getTime();
            //将发布时间大于'最近更新的帖子时间'的帖子内容组织成html
            if (time > lastUpdate){
                lastUpdate  = time;
                var author  = topic.authors[i];
                var authorDetail = topic.authorsMap['_'+author.link+'_'];
                var authorImg = null;
                if (authorDetail && authorDetail.img){
                    authorImg = authorDetail.img
                }
                else{
                    authorImg = "http://otho.douban.com/icon/g15647-1.jpg";
                }
                var content = topic.contents[i];
                var update  = topic.updates[i];
                html.push([
                    '<li class="mbtl">',
                        '<a target="_blank" href="',author.link,'">',
                            '<img src="',authorImg,'" alt="',author.name,'"/>',
                        '</a>', 
                    '</li>',
                    '<li class="mbtrdot">',
                        '<a target="_blank" href="',author.link,'">',author.name,'</a>',
                        (author.city ? ' <span style="color:#666;">('+author.city+')</span>' : ''),
                        ': <br/> ',
                        content, 
                        '<br/><span class="pl">',update,'</span>',
                    '</li>'                
                ].join(''));
            }
        });
        html = html.join('');
        $('#banliao_list').append(html);
        yscroll();
    };
}

/**
 * 数据发送器
 * @param {String} id 话题ID
 * @param {Object} getterObj Getter实例
 * @param {Object} userOptions 用户参数
 */
function Sender(id, getterObj, userOptions){
    var topicId = id;
    var getter = getterObj
    var ck = get_cookie('ck');
    var options = {
        textareaid : '#banliao_input',
        urlPre : URLPRE
    };
    jQuery.extend(options, userOptions);
    
    /**
     * 发送信息
     */
    this.send = function(){
        var url = options.urlPre + topicId + '/add_comment';
        var content = $(options.textareaid).val();
        content = $.trim(content);
        if ( ! content){
            return false;
        }
        //当前话题的回复总页数
        var tp = getter.getTotalPage();
        if (tp > 1){
            var start = (tp - 1) * 100;
        }
        else{
            start = 0;
        }
        $.post(url, {
            ck : get_cookie('ck'),//从Cookie中获取验证码
            rv_comment : content,
            start : start
        },function(r){
            $(options.textareaid).val('');
            $(options.textareaid).focus();
            getter.process(r);
        });
    }
    
}

window.Getter  = Getter;    
window.Updater = Updater;
window.Sender  = Sender;
})(jQuery);